﻿using System;
using System.Collections.Generic;
using System.IO.IsolatedStorage;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Windows.Resources;
using MSPToolkit.Models;
using System.Windows.Media;

namespace MSPToolkit.Utilities
{
    public static class ImageGenerator
    {
        private const string BasicPath = "/Shared/ShellContent/";

        /// <summary>
        /// Renders a collection of TextBlocks on the image
        /// </summary>
        /// <param name="textBlocks">Text blocks that would be rendered</param>
        /// <param name="imageName">Name of the image</param>
        /// <param name="width">Image width</param>
        /// <param name="height">Image height</param>
        /// <param name="imageFormat">Image format</param>
        /// <returns>Uri to the image file</returns>
        public static Uri GenerateImage(IEnumerable<TextBlockWithTransform> textBlocks, string imageName, int width, int height, ImageFormat imageFormat = ImageFormat.Png)
        {
            textBlocks.ThrowExceptionIfArgumentIsNull("textBlocks");
            imageName.ThrowExceptionIfArgumentIsNull("imageName");
            IEnumerable<ImageWithTransform> img = new List<ImageWithTransform>();
            return ImageGenerator.GenerateImageBase(textBlocks, img, imageName, width, height, imageFormat);
        }

        /// <summary>
        /// Renders a collection of Images on the live tile
        /// </summary>
        /// <param name="images">Images that would be rendered</param>
        /// <param name="imageName">Name of the generating image</param>
        /// <param name="width">Image width</param>
        /// <param name="height">Image height</param>
        /// <param name="imageFormat">Image format</param>
        /// <returns>Uri to the image file</returns>
        public static Uri GenerateImage(IEnumerable<ImageWithTransform> images, string imageName, int width, int height, ImageFormat imageFormat = ImageFormat.Png)
        {
            images.ThrowExceptionIfArgumentIsNull("images");
            imageName.ThrowExceptionIfArgumentIsNull("imageName");

            return ImageGenerator.GenerateImageBase(new List<TextBlockWithTransform>(), images, imageName, width, height, imageFormat);
        }

        /// <summary>
        /// Renders collections of TextBlocks and Images on the live tile
        /// </summary>
        /// <param name="textBlocks">TextBlocks that would be rendered</param>
        /// <param name="images">Images that would be rendered</param>
        /// <param name="imageName">Name of the generating image</param>
        /// <param name="width">Image width</param>
        /// <param name="height">Image height</param>
        /// <param name="imageFormat">Image format</param>
        /// <returns>Uri to the image file</returns>
        internal static Uri GenerateImage(IEnumerable<TextBlockWithTransform> textBlocks, IEnumerable<ImageWithTransform> images, string imageName, int width, int height, ImageFormat imageFormat = ImageFormat.Png)
        {
            textBlocks.ThrowExceptionIfArgumentIsNull("textBlocks");
            images.ThrowExceptionIfArgumentIsNull("images");
            imageName.ThrowExceptionIfArgumentIsNull("imageName");

            return ImageGenerator.GenerateImageBase(textBlocks, images, imageName, width, height, imageFormat);
        }

        /// <summary>
        /// Renders collections of TextBlocks and Images on the live tile
        /// </summary>
        /// <param name="textBlocks">TextBlocks that would be rendered</param>
        /// <param name="images">Images that would be rendered</param>
        /// <param name="imageName">Name of the generating image</param>
        /// <param name="width">Image width</param>
        /// <param name="height">Image height</param>
        /// <param name="imageFormat">Image format</param>
        /// <returns>Uri to the image file</returns>
        internal static Uri GenerateImageBase(IEnumerable<TextBlockWithTransform> textBlocks, IEnumerable<ImageWithTransform> images, string imageName, int width, int height, ImageFormat imageFormat = ImageFormat.Png)
        {
            var bitmap = new WriteableBitmap(width, height);

            //render all images
            foreach (var image in images)
            {
                bitmap.Render(image.Image, image.TranslateTransform);
                image.TranslateTransform = null;
                image.Image.RenderTransform = null;
            }

            //render all textBlocks
            foreach (var block in textBlocks)
            {
                bitmap.Render(block.TextBlock, block.TranslateTransform);
                block.TranslateTransform = null;
                block.TextBlock.RenderTransform = null;
            }

            // Invalidate the bitmap to make it actually render
            bitmap.Invalidate();

            var imagePath = BasicPath + imageName;

            switch (imageFormat)
            {
                case ImageFormat.Png:
                    bitmap.SaveWritableBitmapToIsolatedStorageAsPng(imagePath);
                    break;
                case ImageFormat.Jpeg:
                    bitmap.SaveWritableBitmapToIsolatedStorageAsJpg(imagePath);
                    break;
            }

            

            var isoStoreImageUri = new Uri(string.Format("isostore:{0}", imagePath), UriKind.Absolute);

            return isoStoreImageUri;
        }

        /// <summary>
        /// Renders a UIElement on the live tile
        /// </summary>
        /// <param name="uielement">UIElement that would be rendered</param>
        /// <param name="imageName">Name of the generating image</param>
        /// <param name="width">Image width</param>
        /// <param name="height">Image height</param>
        /// <param name="imageFormat">Image format</param>
        /// <returns>Uri to the image file</returns>
        internal static Uri GenerateImageBase(UIElement uielement, string imageName, int width, int height, ImageFormat imageFormat = ImageFormat.Png)
        {
            var bitmap = new WriteableBitmap(width, height);

            bitmap.Render(uielement, new TranslateTransform());

            // Invalidate the bitmap to make it actually render
            bitmap.Invalidate();

            var imagePath = BasicPath + imageName;

            switch (imageFormat)
            {
                case ImageFormat.Png:
                    bitmap.SaveWritableBitmapToIsolatedStorageAsPng(imagePath);
                    break;
                case ImageFormat.Jpeg:
                    bitmap.SaveWritableBitmapToIsolatedStorageAsJpg(imagePath);
                    break;
            }

            var isoStoreImageUri = new Uri(string.Format("isostore:{0}", imagePath), UriKind.Absolute);

            return isoStoreImageUri;
        }

        /// <summary>
        /// Converts StringsWithTransform to TextBlockWithTransform
        /// </summary>
        public static Func<StringsWithTransform, TextBlockWithTransform> StringToTextBlockWithTransform = str =>
        {
            str.ThrowExceptionIfArgumentIsNull("str");

            var textBlock = new TextBlock { Text = str.Text, FontSize = str.FontSize, FontFamily = str.FontFamily, Foreground = str.Foreground };
            return new TextBlockWithTransform { TextBlock = textBlock, TranslateTransform = str.TranslateTransform };
        };

        /// <summary>
        /// Converts UriWithTransform to ImageWithTransform
        /// </summary>
        public static Func<UriWithTransform, ImageWithTransform> UriToImageWithTransform = img =>
        {
            img.ThrowExceptionIfArgumentIsNull("img");
            BitmapImage bitmapImage = new BitmapImage();

            if (img.Uri.IsAbsoluteUri && img.Uri.Scheme == "isostore")
            {
                IsolatedStorageHelper.rwMutex.WaitOne();
                try
                {
                    using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
                    {
                        // Create a stream to store our file in
                        using (var stream = store.OpenFile(img.Uri.AbsolutePath, System.IO.FileMode.Open))
                        {
                            bitmapImage.SetSource(stream);
                            stream.Close();
                        }
                    }
                }
                finally
                {
                    IsolatedStorageHelper.rwMutex.ReleaseMutex();
                }
    
            }
            else
            {
                StreamResourceInfo streamImage = Application.GetResourceStream(img.Uri);
                if (streamImage == null)
                {
                    throw new InvalidOperationException(string.Format("Could not load {0} from resources, did you miss '/AppName;component/' in URI?", img.Uri));
                }
                bitmapImage.SetSource(streamImage.Stream);
            }
            Image image = new Image() { Width = img.Width, Height = img.Height, Source = bitmapImage };
            return new ImageWithTransform { Image = image, TranslateTransform = img.TranslateTransform };
        };

        /// <summary>
        /// Saves a bitmap image to isolated storage as jpg
        /// </summary>
        /// <param name="bitmap">Bitmap that would be saved as the tile image</param>
        /// <param name="imagePath">Path where the tile image would be saved</param>
        private static void SaveWritableBitmapToIsolatedStorageAsJpg(this WriteableBitmap bitmap, string imagePath)
        {
            bitmap.ThrowExceptionIfArgumentIsNull("bitmap");
            bitmap.ThrowExceptionIfArgumentIsNull("string");
            IsolatedStorageHelper.rwMutex.WaitOne();
            try
            {
                using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    // Create a stream to store our file in
                    using (var stream = store.CreateFile(imagePath))
                    {

                        // Save it to the stream
                        bitmap.SaveJpeg(stream, 173, 173, 0, 100);

                        // Close the stream
                        stream.Close();
                    }
                }
            }
            finally
            {
                IsolatedStorageHelper.rwMutex.ReleaseMutex();
            }
        }

        /// <summary>
        /// Saves a bitmap image to isolated storage as png
        /// </summary>
        /// <param name="bitmap">Bitmap that would be saved as the tile image</param>
        /// <param name="imagePath">Path where the tile image would be saved</param>
        private static void SaveWritableBitmapToIsolatedStorageAsPng(this WriteableBitmap bitmap, string imagePath)
        {
            bitmap.ThrowExceptionIfArgumentIsNull("bitmap");
            imagePath.ThrowExceptionIfArgumentIsNull("string");
            IsolatedStorageHelper.rwMutex.WaitOne();
            try
            {
                using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    // Create a stream to store our file in
                    using (var stream = store.CreateFile(imagePath))
                    {

                        // Save it to the stream
                        bitmap.WritePNG(stream);

                        // Close the stream
                        stream.Close();
                    }
                }
            }
            finally
            {
                IsolatedStorageHelper.rwMutex.ReleaseMutex();
            }
        }

        public static void ThrowExceptionIfArgumentIsNull<T>(this T argument, string agrumentName) where T : class
        {
            if (argument == null)
                throw new ArgumentNullException(agrumentName);
        }
    }

    /// <summary>
    /// Supported image formats
    /// </summary>
    public enum ImageFormat
    {
        Png, Jpeg
    }
}